from flask import Flask, render_template, request, redirect, url_for,send_from_directory,send_file
from flask import session,jsonify
import pygame._sdl2 as sdl2
from pygame import mixer
import pygame.mixer
import keyboard
import threading
import datetime
import requests
import socket
import ollama
import random
import shutil
import wave
import json
import time
import os 
import re

app = Flask(__name__)

app.secret_key = ''

with open('config/配置文件/姓.txt','r',encoding = 'utf-8') as f:
    first_names = [word.strip() for word in f.readlines() if word.strip()]

with open('config/配置文件/男名.txt','r',encoding = 'utf-8') as f:
    male_last_names = [word.strip() for word in f.readlines() if word.strip()]

with open('config/配置文件/女名.txt','r',encoding = 'utf-8') as f:
    female_last_names = [word.strip() for word in f.readlines() if word.strip()]

female_names = [a + b + c for a in first_names for b in female_last_names for c in female_last_names]

male_names = [a + b + c for a in first_names for b in male_last_names for c in male_last_names]

yonghu_names = ['用户' + ''.join(random.sample(['一','二','三','四','五','六','七','八','九'],4)) for i in range(10000)]


# 扩展句子变量
def expand_sentence(s):
    global female_names,male_names,yonghu_names

    s = s.replace('|','|')
    
    n = random.uniform(0,1)

    # {昵称}
    for i in range(10):
        if n > 0.66:
            s = re.sub('{昵称}',random.choice(female_names) + random.choice(['宝宝','姐姐']),s, count = 1)
        elif n > 0.33:
            s = re.sub('{昵称}', random.choice(male_names) + random.choice(['宝宝','哥哥']),s, count = 1)
        else:
            s = re.sub('{昵称}', random.choice(yonghu_names) + random.choice(['宝宝']),s, count = 1)
    
    # {xxxx}
    for item in re.findall('\{.+?\}',s):
        if random.random() > 0.5:
            s = s.replace(item,item.strip('{}'),1)
        else:
            s = s.replace(item,'',1)
    
    # <A、B、C、D、E>随机排列组合
    for item in re.findall('<.+?>',s):
        data = item.strip('<>').split('、')
        random.shuffle(data)
        s = s.replace(item,'、'.join(data),1)
    
    # <A，B，C，D，E>随机排列组合
    for item in re.findall('<，+?>',s):
        data = item.strip('<>').split('，')
        random.shuffle(data)
        s = s.replace(item,'，'.join(data),1)
    
    # [A|B|C]随机抽取一个
    for match in re.findall("\[.+?\]", s):
        s = s.replace(match,random.choice(match.strip('[').strip(']').split('|')),1)
    
    # 【A|B|C】随机抽取一个
    for match in re.findall("【.+?】", s):
        s = s.replace(match,random.choice(match.strip('【').strip('】').split('|')),1)
    
    return s.strip()

reply_sents_map = {}

for file in os.listdir('config/配置文件/回复'):
    if not file.endswith('.txt'):
        continue

    reply_sents_map[file.replace('.txt','')] = {
        'lines': [],
        'map': {

        }
    }

    with open(os.path.join('config/配置文件/回复',file),'r',encoding = 'utf-8') as f:
        for line in f.readlines():
            expline = expand_sentence(line)
            reply_sents_map[file.replace('.txt','')]['lines'].append(expline)
            reply_sents_map[file.replace('.txt','')]['map'][expline] = line


# 产品列表
products = [product for product in os.listdir(os.path.join('config','话术脚本')) if os.path.isdir(os.path.join('config','话术脚本',product))]

task_config = {}

for scene in ['scene-1','scene-2','scene-3','scene-4','scene-5']:
    task_config[scene] = {
        'scene_name': scene,
        'current_product': '',
        'current_device': '',
        'startPlayAudio': False,
        'startPlayAudioTask':False,
        'startGetAudio': False,
        'tempScript': [],
        'ip_address': '',
        'active': True if scene == 'scene-1' else False
    }


# 打印分割线
def print_split_line():
    width = os.get_terminal_size().columns
    text = '=' * width
    print(text)


def play_wav(file_path,scene_idx,mode):
    global task_config

    if not os.path.exists(file_path):
        return
    
    pygame.mixer.init(devicename = None if task_config[scene_idx]['current_device'] == '桌面音频' else task_config[scene_idx]['current_device'])

    try:
        pygame.mixer.music.load(file_path)
        pygame.mixer.music.play()

        while pygame.mixer.music.get_busy():
            if not task_config[scene_idx]['startPlayAudio']:
                pygame.mixer.music.pause()
                break

            if mode == 'main':
                # 检测到有临时音频需要播放就马上中断
                if [_ for _ in os.listdir(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'临时',scene_idx)) if _.endswith('.wav')]:
                    pygame.mixer.music.pause()
                    break

        pygame.mixer.music.stop()
        pygame.mixer.music.unload()

    except:
        print(file_path,'**********无法播放**********')
    
    finally:
        os.remove(file_path)

    # 停顿随机变化
    if mode == 'main':
        time.sleep(random.uniform(0,1))

    elif mode == 'temp':
        time.sleep(2)

########################################################################################################################################################################

@app.route('/')
def login():
    return render_template('login.html')


@app.route('/index')
def index():
    mixer.init()
    devices = sdl2.audio.get_audio_device_names(False) + ['桌面音频']
    mixer.quit()

    return render_template('index.html', products = products,devices = devices)


# 开始播放音频
def startPlayAudioTask(scene_idx):
    global task_config

    def get_temp_files():
        global task_config

        return sorted([_ for _ in os.listdir(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'临时',scene_idx)) if _.endswith('.wav')])

    for i in range(10000000):
        files = sorted(os.listdir(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'主播',scene_idx)))

        for file in files:
            if not task_config[scene_idx]['startPlayAudio']:
                while True:
                    if task_config[scene_idx]['startPlayAudio']:
                        break
            
            if not file.endswith('.wav'):
                continue

            # 如果此时临时音频文件夹里有音频文件就先暂停掉主音频的播放
            while get_temp_files():
                play_wav(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'临时',scene_idx,get_temp_files()[0]),scene_idx,'temp')

            file_path = os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'主播',scene_idx,file)

            play_wav(file_path,scene_idx,'main')

        # 这里睡眠0.1秒是为了防止音频过少的时候for循环一下子循环完了
        time.sleep(0.1)


# 开始播放音频
@app.route('/startPlayAudio',methods = ['POST'])
def startPlayAudio():
    global task_config

    scene_idx = request.form.get('scene_idx')

    task_config[scene_idx]['startPlayAudio'] = True

    # 此处这个线程是不能开启多次的，所以要判断是否已经开启
    if not task_config[scene_idx]['startPlayAudioTask']:
        task_config[scene_idx]['startPlayAudioTask'] = True
        
        task_thread = threading.Thread(target = startPlayAudioTask,args = (scene_idx,))
        task_thread.start()

    return jsonify({'success':True,'message':''})


# 停止直播
@app.route('/pausePlayAudio',methods = ['POST'])
def pausePlayAudio():
    global task_config

    scene_idx = request.form.get('scene_idx')

    task_config[scene_idx]['startPlayAudio'] = False

    return jsonify({'success':True,'message':'直播已暂停'})


# 清空音频
@app.route('/clearAudio',methods = ['POST'])
def clearAudio():
    global task_config

    scene_idx = request.form.get('scene_idx')

    if not os.path.exists(os.path.join('config','话术音频',task_config[scene_idx]['current_product'])):
        os.mkdir(os.path.join('config','话术音频',task_config[scene_idx]['current_product']))

    if not os.path.exists(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'主播')):
        os.mkdir(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'主播'))

    if not os.path.exists(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'临时')):
        os.mkdir(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'临时'))

    if not os.path.exists(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'临时',scene_idx)):
        os.mkdir(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'临时',scene_idx))

    if not os.path.exists(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'主播',scene_idx)):
        os.mkdir(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'主播',scene_idx))

    # 先清空历史音频
    for file in os.listdir(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'主播',scene_idx)):
        os.remove(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'主播',scene_idx,file))

    for file in os.listdir(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'临时',scene_idx)):
        os.remove(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'临时',scene_idx,file))

    return jsonify({'success':True,'message':''})


# 选择产品
@app.route('/selectProduct',methods = ['POST'])
def selectProduct():
    global task_config

    scene_idx = request.form.get('scene_idx')
    current_product = request.form.get('product')

    task_config[scene_idx]['current_product'] = current_product

    return jsonify({'success':True,'message':'已切换到' + current_product})


# 选择设备
@app.route('/selectDevice',methods = ['POST'])
def selectDevice():
    global task_config

    scene_idx = request.form.get('scene_idx')
    current_device = request.form.get('device')

    task_config[scene_idx]['current_device'] = current_device

    return jsonify({'success':True,'message':'已切换到' + current_device})



# 选择场景
@app.route('/selectScene',methods = ['POST'])
def selectScene():
    global task_config

    scene_idx = request.form.get('scene_idx')
    
    task_config[scene_idx]['active'] = True


    return jsonify({'success':True,'message':''})


# 开始远程获取主音频
def startGetMainAudioTask(scene_idx):
    global task_config

    if not task_config[scene_idx]['ip_address']:
        return

    url = 'http://' + task_config[scene_idx]['ip_address'] + '/download_main_audio'

    while True:
        # 如果音频库存达到100，直接休眠十分钟，主要是为了让后端有机会去生产文案
        if len(os.listdir(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'主播',scene_idx))) > 100:
            print('库存达到了100，休眠十分钟')
            time.sleep(60 * 10)
            continue

        # 如果暂停直播就退出循环
        if not task_config[scene_idx]['startGetAudio']:
            break
            
        try:
            response = requests.post(url,data = {'scene_idx':scene_idx})
        except:
            print('获取主音频网络请求错误********')
            continue

        if response.status_code == 200:
            save_path = os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'主播',scene_idx,'%s.wav' % datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S"))

            with open(save_path, 'wb') as file:
                file.write(response.content)
            
        elif response.status_code == 404:
            print("主音频文件不存在！")

        else:
            print(f"下载失败，状态码: {response.status_code}")

        time.sleep(1)


# 开始远程获取临时音频
def startGetSubAudioTask(scene_idx):
    global task_config

    if not task_config[scene_idx]['ip_address']:
        return

    url = 'http://' + task_config[scene_idx]['ip_address'] + '/download_sub_audio'

    while True:
        if not task_config[scene_idx]['startGetAudio']:
            break

        try:
            response = requests.post(url,data = {'scene_idx':scene_idx})
        except:
            print('获取临时音频网络请求错误********')
            continue

        if response.status_code == 200:
            save_path = os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'临时',scene_idx,'%s.wav' % datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S"))

            with open(save_path, 'wb') as file:
                file.write(response.content)
            
        elif response.status_code == 404:
            print("临时音频文件不存在！")

        else:
            print(f"下载失败，状态码: {response.status_code}")

        time.sleep(1)


# 开始获取音频
@app.route('/startGetAudio',methods = ['POST'])
def startGetAudio():
    global task_config

    scene_idx = request.form.get('scene_idx')
    ip_address = request.form.get('ip_address')

    task_config[scene_idx]['startGetAudio'] = True
    task_config[scene_idx]['ip_address'] = ip_address
    
    print('开始向',ip_address,scene_idx,'获取音频')

    # 开始获取主音频
    task_thread = threading.Thread(target = startGetMainAudioTask,args = (scene_idx,))
    task_thread.start()

    # 开始获取副音频
    task_thread = threading.Thread(target = startGetSubAudioTask,args = (scene_idx,))
    task_thread.start()

    return jsonify({'success':True,'message':''})


# 暂停获取音频
# @app.route('/stopGetAudio',methods = ['POST'])
# def stopGetAudio():
#     global task_config

#     scene_idx = request.form.get('scene_idx')

#     task_config[scene_idx]['startGetAudio'] = False

#     return jsonify({'success':True,'message':''})


# 同步配置信息
@app.route('/update_task_config',methods = ['POST'])
def update_task_config():
    global task_config

    return jsonify(task_config)


# 获取插话列表
@app.route('/getReplySents',methods = ['POST'])
def getReplySents():
    global task_config,reply_sents_map

    product = request.form.get('product')  

    if product in reply_sents_map:
        reply_sents =  reply_sents_map[product]['lines']
    else:
        reply_sents = []

    return jsonify({'success':True,'data':reply_sents})


# 发送插话
@app.route('/sendReplySent',methods = ['POST'])
def sendReplySent():
    global task_config,reply_sents_map

    scene_idx = request.form.get('scene_idx')
    product = request.form.get('product')
    content = request.form.get('content')
    ip_address = request.form.get('ip_address')
    
    # 转发到服务端
    requests.post('http://' + ip_address + '/sendReplySent',data = {
        'scene_idx':scene_idx,
        'product':product,
        'content':content
    })   

    return jsonify({'success':True,'message':''})


# 查询音频库存
@app.route('/getAudioCount',methods = ['POST'])
def getAudioCount():
    global task_config,reply_sents_map

    scene_idx = request.form.get('scene_idx')

    if not task_config[scene_idx]['current_product']:
        return jsonify({'success':True,'count': -1})
    
    if not os.path.exists(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'主播',scene_idx)):
        return jsonify({'success':True,'count': -1})
    
    data = [file for file in os.listdir(os.path.join('config','话术音频',task_config[scene_idx]['current_product'],'主播',scene_idx))]

    return jsonify({'success':True,'count':len(data)})


def on_key_event(event):
    global task_config

    if event.name == 'f8':
        for scene_idx in task_config:
            if task_config[scene_idx]['active']:
                task_config[scene_idx]['startPlayAudio'] = not task_config[scene_idx]['startPlayAudio']


def start_listen_keyboard():
    keyboard.on_press(on_key_event)


def init_struct():
    for prod in os.listdir(os.path.join('config','话术脚本')):
        if prod.startswith('.'):
            continue

        if prod == '通用':
            continue

        if not os.path.exists(os.path.join('config','话术音频',prod)):
            os.mkdir(os.path.join('config','话术音频',prod))

        if not os.path.exists(os.path.join('config','话术音频',prod,'主播')):
            os.mkdir(os.path.join('config','话术音频',prod,'主播'))

        if not os.path.exists(os.path.join('config','话术音频',prod,'临时')):
            os.mkdir(os.path.join('config','话术音频',prod,'临时'))

        if not os.path.exists(os.path.join('config','话术音频',prod,'主播','scene-1')):
            os.mkdir(os.path.join('config','话术音频',prod,'主播','scene-1'))

        if not os.path.exists(os.path.join('config','话术音频',prod,'主播','scene-2')):
            os.mkdir(os.path.join('config','话术音频',prod,'主播','scene-2'))

        if not os.path.exists(os.path.join('config','话术音频',prod,'主播','scene-3')):
            os.mkdir(os.path.join('config','话术音频',prod,'主播','scene-3'))

        if not os.path.exists(os.path.join('config','话术音频',prod,'主播','scene-4')):
            os.mkdir(os.path.join('config','话术音频',prod,'主播','scene-4'))

        if not os.path.exists(os.path.join('config','话术音频',prod,'主播','scene-5')):
            os.mkdir(os.path.join('config','话术音频',prod,'主播','scene-5'))

        if not os.path.exists(os.path.join('config','话术音频',prod,'临时','scene-1')):
            os.mkdir(os.path.join('config','话术音频',prod,'临时','scene-1'))

        if not os.path.exists(os.path.join('config','话术音频',prod,'临时','scene-2')):
            os.mkdir(os.path.join('config','话术音频',prod,'临时','scene-2'))

        if not os.path.exists(os.path.join('config','话术音频',prod,'临时','scene-3')):
            os.mkdir(os.path.join('config','话术音频',prod,'临时','scene-3'))

        if not os.path.exists(os.path.join('config','话术音频',prod,'临时','scene-4')):
            os.mkdir(os.path.join('config','话术音频',prod,'临时','scene-4'))

        if not os.path.exists(os.path.join('config','话术音频',prod,'临时','scene-5')):
            os.mkdir(os.path.join('config','话术音频',prod,'临时','scene-5'))


if __name__ == '__main__':
    init_struct()

    task_thread = threading.Thread(target = start_listen_keyboard)
    task_thread.start()
    
    app.run(debug = True,port = 6868,host = '0.0.0.0')
